package dbservice

import (
	"database/sql/driver"
	"fmt"
	"reflect"
	"src/protoMsg"
	"src/zeus/common"
	"src/zeus/dbservice/redislib"
	"src/zeus/logger"
	"strings"
)

/*
	本文件下是对需要保存到DBserver上的数据进行redis操作
*/

// 用与redis保存数据的接口
type IRedisValuer interface {
	RedisValue() (driver.Value, error)
}

type IData interface {
	// KeyName 数据的唯一名字
	KeyName() string
	// TableName 拿到表名
	TableName() string
	// Marshal 序列化
	Marshal() []byte
}

// group里服务器使用keyname进行操作的方法，会按PID进行分组
// GroupPlayer:123
func getGroupPlayer(pid uint64, tname string) string {
	if tname == "" {
		return fmt.Sprint("P:", pid)
	} else {
		return fmt.Sprint("P:", pid, ":", tname)
	}
}

// 从keyname中获取表名
func getTableName(keyname string) string {
	return strings.SplitN(keyname, ":", 2)[0]

}

// 分解KeyName
func GetKeyNameResolve(keyname string) (table string, uid uint64) {
	tmpli := strings.Split(keyname, ":")
	if len(tmpli) == 2 {
		uid = common.NewString(tmpli[1]).ToUint64V()
	}
	return tmpli[0], uid
}

type DataUtil struct {
	pid uint64
}

func (utilmd *DataUtil) Key(tname string) string {
	return getGroupPlayer(utilmd.pid, tname)
}

func GetDataUtil(pid uint64) *DataUtil {
	return &DataUtil{pid: pid}
}

// 批量设置行数据
func (utilmd *DataUtil) SetDatas(pmsgs []*protoMsg.DBDataModel) error {
	rd := GetDBPlayerRedis()
	defer rd.Dispose()
	argsli := make(map[string]redislib.Args)
	for _, v := range pmsgs {
		tname, uid := GetKeyNameResolve(v.KeyName)
		if args, ok := argsli[tname]; ok {
			args[common.NewStringAny(uid).ToString()] = v.MsgContent
			// args = args.Add(uid, v.MsgContent)
			argsli[tname] = args
		} else {
			args = make(redislib.Args)
			args[common.NewStringAny(uid).ToString()] = v.MsgContent
			// args = make(redis.Args, 0, len(pmsgs)*2)
			// args = args.Add(uid, v.MsgContent)
			argsli[tname] = args
		}
	}
	for tname, args := range argsli {
		if _, err := rd.HMSet(rd.Ctx, utilmd.Key(tname), args).Result(); err != nil {
			return err
		}

	}
	return nil

}

// 删除指定表的行数据，可以从keyname里获取tname
func (utilmd *DataUtil) DelData(keyname string) {
	rd := GetDBPlayerRedis()
	defer rd.Dispose()
	tname, uid := GetKeyNameResolve(keyname)
	rd.HDel(rd.Ctx, utilmd.Key(tname), common.NewStringAny(uid).ToString())
}

// 删除指定表的一些行
func (utilmd *DataUtil) DelDatas(tname string, keynames []string) {
	if len(keynames) == 0 {
		return
	}
	rd := GetDBPlayerRedis()
	defer rd.Dispose()
	rd.HDel(rd.Ctx, utilmd.Key(tname), keynames...)
}

// 删除标记为需要删的数据行
func (utilmd *DataUtil) DelDataArray(mli []*protoMsg.DBDataModel) {
	if len(mli) == 0 {
		return
	}
	kmapli := make(map[string][]string)
	for _, vmd := range mli {
		tname, uid := GetKeyNameResolve(vmd.KeyName)
		if kli, ok := kmapli[tname]; ok {
			kli = append(kli, common.NewStringAny(uid).ToString())
			kmapli[tname] = kli
		} else {
			kli = make([]string, 1, 10)
			kli[0] = common.NewStringAny(uid).ToString()
			kmapli[tname] = kli
		}
	}
	for tname, keys := range kmapli {
		utilmd.DelDatas(tname, keys)
	}
}

// //----------------------------

// /*
// 单行数据，标脏字段的操作方法
// 以下的方法是针对一行数据的，每个字段都需要单独更新的时候，才使用的的逻辑
// */

// //单行数据获取
// func (utilmd *DataUtil) SingleGet(keyname string) (result map[string][]byte, err error) {
// 	rd := GetDBPlayerRedis()
// 	defer rd.Dispose()
// 	result, err = rd.HgetallByBytesMap(keyname)
// 	return
// }

// //设置数据到redis
// func (utilmd *DataUtil) SingleSet(keyname string, li map[string]interface{}) {
// 	rd := GetDBPlayerRedis()
// 	defer rd.Dispose()
// 	args := make(redis.Args, 0, len(li)*2)
// 	for k, v := range li {
// 		args = args.Add(k, v)
// 	}
// 	rd.Hmset(keyname, args...)
// }

// //删除单行数据（正常应该不会用到这个）
// func (utilmd *DataUtil) SingleDel(keyname string) {
// 	rd := GetDBPlayerRedis()
// 	defer rd.Dispose()
// 	rd.Del(keyname)
// }

//-----------------------------

/*
多行数据:一个用户有多行，同时支持标脏字段的操作方法
以下的方法是针对一个用户多行数据的，每个字段都需要单独更新的时候，才使用的的逻辑
*/
//[多行数据]获取
func (utilmd *DataUtil) RowsSingleGet(table string) (result map[string][]byte, err error) {
	rd := GetDBPlayerRedis()
	defer rd.Dispose()
	result, err = rd.HgetallByBytesMap(utilmd.Key(table))
	return
}

// [多行数据]写入
func (utilmd *DataUtil) RowsSingleSet(table string, pid, uid uint64, li map[string]reflect.Value) error {
	rd := GetDBPlayerRedis()
	defer rd.Dispose()
	// args := make(redis.Args, 0, len(li)*2)
	args := make(map[string]interface{})
	for modname, v := range li {
		defOpt := func(val interface{}) {
			if uid == 0 {
				args[modname] = val
				// args = args.Add(modname, v.Interface())
			} else {
				args[utilmd.GetFieldKey(uid, modname)] = val
				// args = args.Add(utilmd.GetFieldKey(uid, modname), v.Interface())
			}
		}
		switch v.Kind() {
		case reflect.Ptr:
			//指针类型
			if v.IsNil() {
				//val == nil || v.Kind() == reflect.Ptr && v.IsNil()
				if table != "items" {
					logger.Debugf(logger.LogKey_LoginFlow, "val==nil table:%s uid:%d modname:%s li:%v", table, uid, modname, li)
				}
				//NOTE: redis 存储 nil=="" 字符串,没有"nil" 反序列化还是会有值得.
				if uid == 0 {
					args[modname] = ""
					// args = args.Add(modname, "")
				} else {
					args[utilmd.GetFieldKey(uid, modname)] = ""
					// args = args.Add(utilmd.GetFieldKey(uid, modname), "")
				}
				break
			}
			//正常指针类型
			if val, ok := v.Elem().Interface().(driver.Valuer); ok {
				tmp, _ := val.Value()
				if uid == 0 {
					args[modname] = tmp
					// args = args.Add(modname, tmp)
				} else {
					args[utilmd.GetFieldKey(uid, modname)] = tmp
					// args = args.Add(utilmd.GetFieldKey(uid, modname), tmp)
				}
				break
			}
			// goto default,but can't use fallthrough
			defOpt(v.Interface())
		case reflect.Struct:
			if val, ok := v.Interface().(driver.Valuer); ok {
				tmp, _ := val.Value()
				if uid == 0 {
					args[modname] = tmp
					// args = args.Add(modname, tmp)
				} else {
					args[utilmd.GetFieldKey(uid, modname)] = tmp
					// args = args.Add(utilmd.GetFieldKey(uid, modname), tmp)
				}
				break
			}
			//defOpt()
			fallthrough
		case reflect.Int32:
			//有可能是枚举
			defOpt(v.Int())
			// if modname == "State" {
			// 	defOpt(v.Int())
			// } else {
			// 	defOpt(v.Interface())
			// }
		default:
			// kind := v.Kind()
			// logger.Infof("RowsSingleSet %s:%d", modname, kind)
			defOpt(v.Interface())
		}
	}
	return rd.HMSet(rd.Ctx, utilmd.Key(table), args).Err()
}

func (utilmd *DataUtil) RowsSingleDels(table string, uid uint64, li map[string]reflect.Value) {
	rd := GetDBPlayerRedis()
	defer rd.Dispose()
	args := make([]string, 0, len(li)*2)
	for modname := range li {
		if uid == 0 {
			args = append(args, modname)
		} else {
			args = append(args, utilmd.GetFieldKey(uid, modname))
		}
	}
	rd.HDel(rd.Ctx, utilmd.Key(table), args...)
}

// 获取字段名字和主键ID
func (utilmd *DataUtil) GetKeySplitToFieldName(key string) (id uint64, field string) {
	var err error
	if tmpli := strings.Split(key, ":"); len(tmpli) == 1 {
		if id, err = common.NewString(tmpli[0]).ToUint64(); err != nil {
			//如果不能转成ID应该就是单行多字段
			field = tmpli[0]
		}

	} else if len(tmpli) > 1 {
		id = common.NewString(tmpli[0]).ToUint64V()
		field = tmpli[1]
	}
	return
}

// 获取字段Key
func (utilmd *DataUtil) GetFieldKey(id uint64, modname string) (key string) {
	return fmt.Sprintf("%d:%s", id, modname)
}
